package cyou.breathe.dynamictp.spring.base.support;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import cyou.breathe.dynamictp.core.executor.BreatheThreadExecutor;
import cyou.breathe.dynamictp.core.executor.BreatheThreadRegistry;
import cyou.breathe.dynamictp.core.executor.ThreadPoolExecutorProperties;
import cyou.breathe.dynamictp.core.executor.support.BlockingQueueTypeEnum;
import cyou.breathe.dynamictp.core.executor.support.RejectedPolicyTypeEnum;
import cyou.breathe.dynamictp.spring.base.BreatheDynamicTp;
import cyou.breathe.dynamictp.core.config.BootstrapConfigProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * 动态线程池后置处理器，扫描 Bean 是否为动态线程池，如果是的话进行属性填充和注册
 * @author: breathe
 * @createTime: 2025-09-14
 */
@Slf4j
@RequiredArgsConstructor
public class BreatheThreadBeanPostProcessor implements BeanPostProcessor {

    private final BootstrapConfigProperties properties;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof BreatheThreadExecutor) {
            BreatheDynamicTp dynamicThreadPool;
            try {
                // 通过 IOC 容器扫描 Bean 是否存在动态线程池注解
                dynamicThreadPool = ApplicationContextHolder.findAnnotationOnBean(beanName, BreatheDynamicTp.class);
                if (Objects.isNull(dynamicThreadPool)) {
                    return bean;
                }
            } catch (Exception ex) {
                log.error("无法利用注解创建动态线程池", ex);
                return bean;
            }

            BreatheThreadExecutor breatheThreadExecutor = (BreatheThreadExecutor) bean;
            // 从配置中心读取动态线程池配置并对线程池进行赋值
            ThreadPoolExecutorProperties executorProperties = properties.getExecutors()
                    .stream()
                    .filter(each -> Objects.equals(breatheThreadExecutor.getThreadPoolId(), each.getThreadPoolId()))
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("配置中该线程池ID不存在."));
            overrideLocalThreadPoolConfig(executorProperties, breatheThreadExecutor);

            // 注册到动态线程池注册器，后续监控和报警从注册器获取线程池实例。同时，参数动态变更需要依赖 ThreadPoolExecutorProperties 比对是否有边跟
            BreatheThreadRegistry.putHolder(breatheThreadExecutor.getThreadPoolId(), breatheThreadExecutor, executorProperties);
        }

        return bean;
    }

    private void overrideLocalThreadPoolConfig(ThreadPoolExecutorProperties executorProperties, BreatheThreadExecutor breatheThreadExecutor) {
        Integer remoteCorePoolSize = executorProperties.getCorePoolSize();
        Integer remoteMaximumPoolSize = executorProperties.getMaximumPoolSize();
        Assert.isTrue(remoteCorePoolSize <= remoteMaximumPoolSize, "remoteCorePoolSize 必须小于 remoteMaximumPoolSize.");

        //要保证核心线程数和最大线程数的正确性, 不能使核心线程比最大线程大
        int originalMaximumPoolSize = breatheThreadExecutor.getMaximumPoolSize();
        if (remoteCorePoolSize > originalMaximumPoolSize) {
            breatheThreadExecutor.setMaximumPoolSize(remoteMaximumPoolSize);
            breatheThreadExecutor.setCorePoolSize(remoteCorePoolSize);
        } else {
            breatheThreadExecutor.setCorePoolSize(remoteCorePoolSize);
            breatheThreadExecutor.setMaximumPoolSize(remoteMaximumPoolSize);
        }

        // 阻塞队列没有常规 set 方法，所以使用反射赋值
        BlockingQueue workQueue = BlockingQueueTypeEnum.createBlockingQueue(executorProperties.getWorkQueue(), executorProperties.getQueueCapacity());

        ReflectUtil.setFieldValue(breatheThreadExecutor, "workQueue", workQueue);

        // 赋值动态线程池其他核心参数
        breatheThreadExecutor.setKeepAliveTime(executorProperties.getKeepAliveTime(), TimeUnit.SECONDS);
        breatheThreadExecutor.allowCoreThreadTimeOut(executorProperties.getAllowCoreThreadTimeOut());
        breatheThreadExecutor.setRejectedExecutionHandler(RejectedPolicyTypeEnum.createPolicy(executorProperties.getRejectedHandler()));
    }
}
